﻿#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Xml;
using UnityEditor;
/// <summary>
/// 编辑器Assembly
/// </summary>
public partial class ESF_Utility
{
    #region GetType
    /// <summary>
    /// 类别映射
    /// Key: Type Id
    /// Value: Type
    /// </summary>
    static Dictionary<int, Type> mGetType_TypeMaping = new Dictionary<int, Type>();
    /// <summary>
    /// 程序集映射
    /// Key: Type Id
    /// Value: Assembly
    /// </summary>
    static Dictionary<int, Assembly> mGetType_AssemblyMaping = new Dictionary<int, Assembly>();
    /// <summary>
    /// 缓存程序集
    /// </summary>
    static List<Assembly> mCacheAssemblies = new List<Assembly>();

    /// <summary>
    /// 加载所有Assembly程序集
    /// </summary>
    static void OnLoadAllAssemblies()
    {
        if (mCacheAssemblies == null || mCacheAssemblies.Count <= 0)
        {
            ESF_FileExtAttribute attr = enESF_UnityFileExt.Dll.ESF_FirstAttribute<ESF_FileExtAttribute>();
            string[] dlls = Directory.GetFiles(globalLibraryScriptAssembliesFolderPath, attr.searchPattern);
            List<string> paths = new List<string>();
            if (dlls != null && dlls.Length > 0)
            {
                foreach (string d in dlls)
                {
                    if (!paths.Contains(d))
                    {
                        paths.Add(d);
                    }
                }
            }
            dlls = Directory.GetFiles(globalUnityManagedDataDllPath, attr.searchPattern);
            if (dlls != null && dlls.Length > 0)
            {
                foreach (string d in dlls)
                {
                    if (!paths.Contains(d))
                    {
                        paths.Add(d);
                    }
                }
            }            

            AssemblyName[]  ans = Assembly.GetExecutingAssembly().GetReferencedAssemblies();
            if (ans != null)
            {
                foreach (var key in ans)
                {
                    Assembly am = Assembly.Load(key);
                    if (!paths.Contains(am.Location))
                    {
                        paths.Add(am.Location);
                    }                    
                }
            }

            foreach (var p in paths)
            {
                mCacheAssemblies.Add(Assembly.LoadFile(p));
            }
        }
    }

    /// <summary>
    /// 获得指定类名的Type
    /// </summary>
    /// <param name="_typeName">类别名称</param>
    /// <param name="_assembly">程序集</param>
    /// <returns>Type</returns>
    public static Type GetType(string _typeName, ref Assembly _assembly)
    {
        int key = _typeName.GetHashCode();
        Type type = null;
        if (!mGetType_TypeMaping.ContainsKey(key))
        {
            OnLoadAllAssemblies();
            if (mCacheAssemblies != null && mCacheAssemblies.Count > 0)
            {
                foreach (Assembly m in mCacheAssemblies)
                {
                    type = m.GetType(_typeName);
                    if (type == null)
                    {
                        type = m.GetType(m.GetName().Name + "." + _typeName);
                    }
                    if (type != null)
                    {
                        _assembly = m;
                        break;
                    }
                }
            }
            mGetType_TypeMaping.Add(key, type);
            mGetType_AssemblyMaping.Add(key, _assembly);
        }
        else
        {
            type = mGetType_TypeMaping[key];
            _assembly = mGetType_AssemblyMaping[key];
        }
        return type;
    }

    /// <summary>
    /// 获得所有Type
    /// </summary>
    /// <param name="_funcType">是否是有效Type</param>
    /// <returns>Type组</returns>
    public static List<Type> GetTypes(Func<Type,bool> _funcType = null)
    {
        List<Type> result = new List<Type>();
        OnLoadAllAssemblies();
        if (mCacheAssemblies != null && mCacheAssemblies.Count > 0)
        {
            foreach (Assembly m in mCacheAssemblies)
            {
                Type[] types = m.GetExportedTypes();
                if (types != null)
                {
                    foreach (Type t in types)
                    {
                        if (_funcType == null || _funcType.Invoke(t))
                        {
                            result.Add(t);
                        }
                    }
                }
            }
        }
        return result;
    }
    #endregion

    #region CompileCSProjToDll 编译csproj为dll文件
    /// <summary>
    /// 编译csproj为dll文件
    /// </summary>
    /// <param name="_csprojPath">.csproj文件路径</param>
    /// <param name="_destDllBytesFilePath">目标Dll数据文件路径</param>
    /// <param name="_logFolder">日志文件夹</param>
    public static void CompileCSProjToDll(string _csprojPath,string _destDllBytesFilePath,string _logFolder)
    {
        string srcCSProj = Path.GetFullPath(_csprojPath).ESF_TransPathSeparatorCharToUnityChar();
        string name = Path.GetFileNameWithoutExtension(_csprojPath);        
        string srcDllFile = Path.Combine(_logFolder, $"{name}{enESF_UnityFileExt.Dll.ExtAttr().ext}").ESF_TransPathSeparatorCharToUnityChar();
        //string destCSProj = Path.Combine(Environment.GetFolderPath( Environment.SpecialFolder.Templates),Path.GetFileName(_csprojPath));
        //File.Copy(srcCSProj, destCSProj, true);
        string defineConstants = OnCombineCSProjDefineConstants(srcCSProj, true);
        //ApiCompatibilityLevel level  = PlayerSettings.GetApiCompatibilityLevel(EditorUserBuildSettings.selectedBuildTargetGroup);
        string code = File.ReadAllText(ESF_enResxTempleteFile.Bat_MSBuild_Templete_txt);
        code = code.CompilerCSharp(new
        {
            logFolderName = Path.GetFileName(_logFolder),
            defineConstants,
            outFolder = _logFolder,
            csproj = srcCSProj
        });
        ExcuteAsBat(code);
        File.Copy(srcDllFile, _destDllBytesFilePath, true);
        ExecuteMenuAssetsRefresh();
    }

    /// <summary>
    /// 合并CSProj文件的DefineConstants
    /// </summary>
    /// <param name="_csprojPath">csproj文件路径</param>
    /// <param name="_includeProjectReference">包含ProjectReference引用的csproj工程</param>
    /// <returns>DefineConstants</returns>
    static string OnCombineCSProjDefineConstants(string _csprojPath, bool _includeProjectReference = false)
    {
        string result = string.Empty;
        List<string> csprojDefineConstants = OnGetCSProjDefineConstants(_csprojPath, _includeProjectReference);
        if (csprojDefineConstants != null && csprojDefineConstants.Count > 0)
        {
            List<string> defines = new List<string>();
            var dcs = csprojDefineConstants.Distinct();
            foreach (var d in dcs)
            {
                string[] vals = d.ESF_Split(enESF_SeparatorSymbol.Colon);
                if (vals != null && vals.Length > 0)
                {
                    defines.AddRange(vals);
                }
            }

            if (defines.Count > 0)
            {
                var defs = defines.Distinct().ToList();
                result = defs.ESF_Join(enESF_SeparatorSymbol.Colon);
            }
        }
        return result;
    }

    /// <summary>
    /// 获得CSProj文件的DefineConstants
    /// </summary>
    /// <param name="_csprojPath">csproj文件路径</param>
    /// <param name="_includeProjectReference">包含ProjectReference引用的csproj工程</param>
    /// <returns>DefineConstants</returns>
    static List<string> OnGetCSProjDefineConstants(string _csprojPath,bool _includeProjectReference = false)
    {
        List<string> result = new List<string>();
        XmlNode defineConstantsNode = OnFindCSProjNodeDefineConstants(_csprojPath);
        string defineConstants = Regex.Replace(defineConstantsNode.InnerText, @"UNITY_EDITOR[_\d\w]*;?", "");
        defineConstants = Regex.Replace(defineConstants, @"[_\d\w]*_EDITOR;?", "");
        result.Add(defineConstants);
        if (_includeProjectReference)
        {
            XmlNodeList projRefs = OnFindCSProjNodeProjectReference(_csprojPath);
            if (projRefs != null && projRefs.Count > 0)
            {
                string folder = Path.GetDirectoryName(_csprojPath);
                foreach (XmlNode n in projRefs)
                {
                    string p = n.Attributes["Include"].Value;
                    List<string> vals = OnGetCSProjDefineConstants(Path.Combine(folder, p).ESF_TransPathSeparatorCharToUnityChar(), _includeProjectReference);
                    if (vals != null && vals.Count > 0)
                    {
                        result.AddRange(vals);
                    }
                }
            }
        }        
        return result;
    }

    /// <summary>
    /// 获得CSProj文件的DefineConstants节点
    /// </summary>
    /// <param name="_csprojPath">csproj文件路径</param>
    /// <returns>DefineConstants节点</returns>
    static XmlNode OnFindCSProjNodeDefineConstants(string _csprojPath)
    {
        XmlDocument doc = new XmlDocument();
        doc.Load(_csprojPath);
        string ns = doc.DocumentElement.GetAttribute("xmlns");
        XmlNamespaceManager nsMgr = new XmlNamespaceManager(doc.NameTable);
        nsMgr.AddNamespace("ns", ns);
        XmlNode dcn = doc.SelectSingleNode("ns:Project/ns:PropertyGroup/ns:DefineConstants", nsMgr);
        return dcn;
    }

    /// <summary>
    /// 获得CSProj文件的ProjectReference节点
    /// </summary>
    /// <param name="_csprojPath">csproj文件路径</param>
    /// <returns>ProjectReference节点</returns>
    static XmlNodeList OnFindCSProjNodeProjectReference(string _csprojPath)
    {
        XmlDocument doc = new XmlDocument();
        doc.Load(_csprojPath);
        string ns = doc.DocumentElement.GetAttribute("xmlns");
        XmlNamespaceManager nsMgr = new XmlNamespaceManager(doc.NameTable);
        nsMgr.AddNamespace("ns", ns);
        XmlNodeList prs = doc.SelectNodes("ns:Project/ns:ItemGroup/ns:ProjectReference", nsMgr);
        return prs;
    }
    #endregion
}
#endif
